﻿using System;
using System.Collections.Generic;
using NAnt.Core;
using NAnt.Core.Attributes;
using System.IO;
using NAnt.SVN.Util;
using NAnt.SVN.Tasks;
using NAnt.SVN.UtilTasks.Constants;

namespace NAnt.SVN.UtilTasks
{
    /// <summary>
    /// Use this class to get the buildnumber of a project. This tasks
    /// scans through the files looking for AssemblyInfo.
    /// Run the setbuildnumber task in NAnt.
    /// $Id$
    /// </summary>
    [TaskName("setbuildnumber")]
    public sealed class SetBuildNumber : BaseExtensionTask
    {
        #region Private Fields

        //Internal task flags
        private bool recursive = true;
        string[] assemblyinfofiles;
        bool assemblyinfofilesfound = false;

        #endregion

        #region Public Fields

        /// <summary>
        /// Check the files recursively
        /// </summary>
        [TaskAttribute("recursive", Required = false)]
        public bool Recursive
        {
            get { return recursive; }
            set { recursive = value; }
        }

        #endregion

        /// <summary>
        /// Main exec function
        /// </summary>
        protected override void ExecuteTask()
        {
            //Search the assembly info files
            assemblyinfofilesfound = searchAssemblyInfoFiles();
            //If certain properties such as revision and fileversion doesn't exist: call
            //call the GetBuildNumber task
            if (!PropertiesManager.IsSVNProperty(Resources.PropertyNames.FileVersion) ||
                !PropertiesManager.IsSVNProperty(Resources.PropertyNames.Revision))
            {
                GetBuildNumber get = new GetBuildNumber();
                this.CopyTo(get);
                get.Recursive = Recursive;
                get.Execute();
            }

            //Parse and write the files
            if (assemblyinfofilesfound)
            {
                foreach (string file in assemblyinfofiles)
                {
                    parseAssemblyInfofile(file);
                }
            }
            else
            {
                Log(Level.Warning, Resources.Strings.UTIL0307);
            }
        }

        #region Support Functions

        /// <summary>
        /// Parse the assembly file info
        /// </summary>
        /// <param name="file"></param>
        private void parseAssemblyInfofile(string file)
        {
            if (File.Exists(file))
            {
                Log(Level.Verbose, string.Format(Resources.Strings.UTIL0203, file));
                //Read the file and store it in a list
                List<string> filebuffer = new List<string>();
                string line;
                StreamReader read = new StreamReader(file);
                //Read and interpolate the file
                while ((line = read.ReadLine()) != null)
                {
                    if (line.Contains(AssemblyInfoParts.ASSEMBLYVERSION) && !isCommentline(line))
                    {
                        line = makeAttributeLine(AssemblyInfoParts.ASSEMBLYVERSION,
                            PropertiesManager.GetSVNProperty(Resources.PropertyNames.Major) + "." +
                            PropertiesManager.GetSVNProperty(Resources.PropertyNames.Minor) + "." +
                            PropertiesManager.GetSVNProperty(Resources.PropertyNames.Build) + "." +
                            PropertiesManager.GetSVNProperty(Resources.PropertyNames.Revision)
                        );
                    }
                    else if (line.Contains(AssemblyInfoParts.ASSEMBLYFILEVERSION) && !isCommentline(line))
                    {
                        line = makeAttributeLine(AssemblyInfoParts.ASSEMBLYFILEVERSION, PropertiesManager.GetSVNProperty(Resources.PropertyNames.FileVersion));
                    }
                    filebuffer.Add(line);
                }
                read.Close();
                //Write to file
                StreamWriter write = new StreamWriter(file);
                foreach (string l in filebuffer)
                {
                    write.WriteLine(l);
                }
                write.Close();
            }
            else
                Log(Level.Error, string.Format(Resources.Strings.SVNFILENOTFOUND, file));
        }

        /// <summary>
        /// Search the assembly info files
        /// </summary>
        private bool searchAssemblyInfoFiles()
        {
            //Search files
            SearchOption so = (Recursive) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
            assemblyinfofiles = Directory.GetFiles(Project.BaseDirectory, GetBuildNumber.ASSEMBLYINFO, so);
            //Log it
            Log(Level.Verbose, string.Format(Resources.Strings.UTIL0201, assemblyinfofiles.Length));
            foreach (string f in assemblyinfofiles) { Log(Level.Verbose, string.Format(Resources.Strings.UTIL0202, f)); }
            //Set flag
            return (assemblyinfofiles.Length > 0);
        }

        /// <summary>
        /// Remake the attribute
        /// </summary>
        /// <param name="attribute"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private string makeAttributeLine(string attribute, string value)
        {
            return "[assembly: " + attribute + "(\"" + value + "\")]";
        }

        /// <summary>
        /// Check if a line is a comment
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        private bool isCommentline(string line)
        {
            if (line.Length < 2) return false;
            else return line.Substring(0, 2).Equals("//");
        }

        #endregion
    }
}
